/*
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with this
 * work for additional information regarding copyright ownership. The ASF
 * licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 * INCLUDES MODIFICATIONS BY GOOGLE.
 */
/** author Elena Semukhina */
package com.google.j2cl.jre.java.math;

import com.google.j2cl.jre.java.util.EmulTestBase;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;

/**
 * Class: java.math.BigDecimal Methods: movePointLeft, movePointRight, scale,
 * setScale, unscaledValue *.
 */
public class BigDecimalScaleOperationsTest extends EmulTestBase {

  public void testScaleByPowerOfTen() {
    BigDecimal val = BigDecimal.ONE.scaleByPowerOfTen(1);
    assertEquals(10, val.intValue());
  }

  /**
   * Check the default scale.
   */
  public void testScaleDefault() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int cScale = 0;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a));
    assertTrue("incorrect scale", aNumber.scale() == cScale);
  }

  /**
   * Check a negative scale.
   */
  public void testScaleNeg() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = -10;
    int cScale = -10;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    assertTrue("incorrect scale", aNumber.scale() == cScale);
  }

  /**
   * Check a positive scale.
   */
  public void testScalePos() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = 10;
    int cScale = 10;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    assertTrue("incorrect scale", aNumber.scale() == cScale);
  }

  /**
   * Check the zero scale.
   */
  public void testScaleZero() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = 0;
    int cScale = 0;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    assertTrue("incorrect scale", aNumber.scale() == cScale);
  }

  /**
   * Check the unscaled value.
   */
  public void testUnscaledValue() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = 100;
    BigInteger bNumber = new BigInteger(a);
    BigDecimal aNumber = new BigDecimal(bNumber, aScale);
    assertTrue("incorrect unscaled value", aNumber.unscaledValue().equals(
        bNumber));
  }

  /**
   * Set a greater new scale.
   */
  public void testSetScaleGreater() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = 18;
    int newScale = 28;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.setScale(newScale);
    assertTrue("incorrect scale", bNumber.scale() == newScale);
    assertEquals("incorrect value", 0, bNumber.compareTo(aNumber));
  }

  /**
   * Set a less new scale; this.scale == 8; newScale == 5.
   */
  public void testSetScaleLess() {
    String a = "2.345726458768760000E+10";
    int newScale = 5;
    BigDecimal aNumber = new BigDecimal(a);
    BigDecimal bNumber = aNumber.setScale(newScale);
    assertTrue("incorrect scale", bNumber.scale() == newScale);
    assertEquals("incorrect value", 0, bNumber.compareTo(aNumber));
  }

  /**
   * Verify an exception when setting a new scale.
   */
  public void testSetScaleException() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = 28;
    int newScale = 18;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    try {
      aNumber = aNumber.setScale(newScale);
      fail("ArithmeticException has not been caught");
    } catch (ArithmeticException e) {
      assertEquals("Improper exception message", "Rounding necessary",
          e.getMessage());
    }
  }

  /**
   * Set the same new scale.
   */
  public void testSetScaleSame() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = 18;
    int newScale = 18;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.setScale(newScale);
    assertTrue("incorrect scale", bNumber.scale() == newScale);
    assertTrue("incorrect value", bNumber.equals(aNumber));
  }

  /**
   * Set a new scale.
   */
  public void testSetScaleRoundUp() {
    String a = "1231212478987482988429808779810457634781384756794987";
    String b = "123121247898748298842980877981045763478139";
    int aScale = 28;
    int newScale = 18;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_UP);
    assertTrue("incorrect scale", bNumber.scale() == newScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b));
  }

  /**
   * Set a new scale.
   */
  public void testSetScaleRoundDown() {
    String a = "1231212478987482988429808779810457634781384756794987";
    String b = "123121247898748298842980877981045763478138";
    int aScale = 28;
    int newScale = 18;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_DOWN);
    assertTrue("incorrect scale", bNumber.scale() == newScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b));
  }

  /**
   * Set a new scale.
   */
  public void testSetScaleRoundCeiling() {
    String a = "1231212478987482988429808779810457634781384756794987";
    String b = "123121247898748298842980877981045763478139";
    int aScale = 28;
    int newScale = 18;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_CEILING);
    assertTrue("incorrect scale", bNumber.scale() == newScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b));
  }

  /**
   * Set a new scale.
   */
  public void testSetScaleRoundFloor() {
    String a = "1231212478987482988429808779810457634781384756794987";
    String b = "123121247898748298842980877981045763478138";
    int aScale = 28;
    int newScale = 18;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_FLOOR);
    assertTrue("incorrect scale", bNumber.scale() == newScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b));
  }

  /**
   * Set a new scale.
   */
  public void testSetScaleRoundHalfUp() {
    String a = "1231212478987482988429808779810457634781384756794987";
    String b = "123121247898748298842980877981045763478138";
    int aScale = 28;
    int newScale = 18;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_HALF_UP);
    assertTrue("incorrect scale", bNumber.scale() == newScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b));
  }

  /**
   * Set a new scale.
   */
  public void testSetScaleRoundHalfDown() {
    String a = "1231212478987482988429808779810457634781384756794987";
    String b = "123121247898748298842980877981045763478138";
    int aScale = 28;
    int newScale = 18;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_HALF_DOWN);
    assertTrue("incorrect scale", bNumber.scale() == newScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b));
  }

  /**
   * Set a new scale.
   */
  public void testSetScaleRoundHalfEven() {
    String a = "1231212478987482988429808779810457634781384756794987";
    String b = "123121247898748298842980877981045763478138";
    int aScale = 28;
    int newScale = 18;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.setScale(newScale, BigDecimal.ROUND_HALF_EVEN);
    assertTrue("incorrect scale", bNumber.scale() == newScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b));
  }

  /**
   * SetScale(int, RoundingMode).
   */
  public void testSetScaleIntRoundingMode() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = 28;
    int newScale = 18;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal result = aNumber.setScale(newScale, RoundingMode.HALF_EVEN);
    String res = "123121247898748298842980.877981045763478138";
    int resScale = 18;
    assertEquals("incorrect value", res, result.toString());
    assertEquals("incorrect scale", resScale, result.scale());
  }

  /**
   * Move the decimal point to the left; the shift value is positive.
   */
  public void testMovePointLeftPos() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = 28;
    int shift = 18;
    int resScale = 46;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.movePointLeft(shift);
    assertTrue("incorrect scale", bNumber.scale() == resScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(a));
  }

  /**
   * Move the decimal point to the left; the shift value is positive.
   */
  public void testMovePointLeftNeg() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = 28;
    int shift = -18;
    int resScale = 10;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.movePointLeft(shift);
    assertTrue("incorrect scale", bNumber.scale() == resScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(a));
  }

  /**
   * Move the decimal point to the right; the shift value is positive.
   */
  public void testMovePointRightPosGreater() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = 28;
    int shift = 18;
    int resScale = 10;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.movePointRight(shift);
    assertTrue("incorrect scale", bNumber.scale() == resScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(a));
  }

  /**
   * Move the decimal point to the right; the shift value is positive.
   */
  public void testMovePointRightPosLess() {
    String a = "1231212478987482988429808779810457634781384756794987";
    String b = "123121247898748298842980877981045763478138475679498700";
    int aScale = 28;
    int shift = 30;
    int resScale = 0;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.movePointRight(shift);
    assertTrue("incorrect scale", bNumber.scale() == resScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(b));
  }

  /**
   * Move the decimal point to the right; the shift value is positive.
   */
  public void testMovePointRightNeg() {
    String a = "1231212478987482988429808779810457634781384756794987";
    int aScale = 28;
    int shift = -18;
    int resScale = 46;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    BigDecimal bNumber = aNumber.movePointRight(shift);
    assertTrue("incorrect scale", bNumber.scale() == resScale);
    assertTrue("incorrect value", bNumber.unscaledValue().toString().equals(a));
  }

  /**
   * Move the decimal point to the right when the scale overflows.
   */
  public void testMovePointRightException() {
    String a = "12312124789874829887348723648726347429808779810457634781384756794987";
    int aScale = Integer.MAX_VALUE; // 2147483647
    int shift = -18;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    try {
      aNumber = aNumber.movePointRight(shift);
      fail("ArithmeticException has not been caught");
    } catch (ArithmeticException e) {
      assertEquals("Improper exception message", "Underflow", e.getMessage());
    }
  }

  /**
   * precision().
   */
  public void testPrecision() {
    String a = "12312124789874829887348723648726347429808779810457634781384756794987";
    int aScale = 14;
    BigDecimal aNumber = new BigDecimal(new BigInteger(a), aScale);
    int prec = aNumber.precision();
    assertEquals(68, prec);
  }
}
